
#include "Debug.h"
#include "simple_protocol.h"
#include "utility.h"
#include <Reader/Reader.h>

enum {
	API_MIFARE_LOGIN = 0,
	API_MIFARE_READ_BLOCK = 1,
	API_MIFARE_WRITE_BLOCK = 2,
	API_MIFARE_READ_VALUE = 3,
	API_MIFARE_WRITE_VALUE = 4,
	API_MIFARE_INC_VALUE = 5,
	API_MIFARE_DEC_VALUE = 6,
	API_MIFARE_COPY_VALUE = 7,
	API_MIFARE_NUM,
};

phStatus_t mifare_auth(uint8_t sector, uint8_t keyType, const uint8_t* keyBuf){
	phStatus_t status;
	uint8_t ver = 0;
	uint8_t keyNo = 1;
	uint8_t bBlockNo = sector * 4;
	void *psKeyStore = phNfcLib_GetDataParams(PH_COMP_KEYSTORE);
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	uint8_t * uid;
	uint8_t ulen;
	
	if(reader.dwActivatedType != E_PH_NFCLIB_MIFARE_CLASSIC_1K
		&& reader.dwActivatedType != E_PH_NFCLIB_MIFARE_CLASSIC_4K){
		return 0xFF;
	}
	PH_CHECK_SUCCESS_FCT(status, phKeyStore_FormatKeyEntry(psKeyStore, keyNo, PH_KEYSTORE_KEY_TYPE_MIFARE));
	uint16_t off = (keyType == PHHAL_HW_MFC_KEYA) ? 0 : PHHAL_HW_MFC_KEY_LENGTH;
	uint8_t mf_key[PHHAL_HW_MFC_KEY_LENGTH*2] = {0};
	memcpy(mf_key+off, keyBuf, PHHAL_HW_MFC_KEY_LENGTH);
	PH_CHECK_SUCCESS_FCT(status, phKeyStore_SetKeyAtPos(psKeyStore, keyNo, 0,
						 PH_KEYSTORE_KEY_TYPE_MIFARE, (uint8_t *)mf_key, ver));

	LOG_D("**** Set Key Store successful");
	uid = reader.tag.uid;
	ulen = reader.tag.ulen;
	/* Mifare Classic card, send authentication for sector 0 */
	status = phalMfc_Authenticate(psalMFC, bBlockNo, keyType, keyNo, ver, uid, ulen);
	if(status)
	{
		LOG_D("!!! Authentication was not successful."
						 "!!! Please correct the key at line 86.");
		LOG_D("/****** Abort of execution ******/");
		return status;
	}
	LOG_D("**** Authentication successful");
	return status;
}

phStatus_t mifare_read(uint8_t block, uint8_t *data){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_Read(psalMFC, block, data);
}

phStatus_t mifare_write(uint8_t block, uint8_t *data){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_Write(psalMFC, block, data);
}

phStatus_t mifare_read_value(uint8_t block, uint8_t *value){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	uint8_t addr;
	return phalMfc_ReadValue(psalMFC, block, (uint8_t *) value, &addr);
}

phStatus_t mifare_write_value(uint8_t block, uint8_t *value){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_WriteValue(psalMFC, block, (uint8_t *) value, block);
}

phStatus_t mifare_increment_value(uint8_t block, uint8_t *value){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_IncrementTransfer(psalMFC, block, block, (uint8_t *)value);
}

phStatus_t mifare_decrement_value(uint8_t block, uint8_t *value){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_DecrementTransfer(psalMFC, block, block, (uint8_t *)value);
}

phStatus_t mifare_copy_value(uint8_t srcBlock, uint8_t destBlock){
	void *psalMFC = phNfcLib_GetDataParams(PH_COMP_AL_MFC);
	return phalMfc_RestoreTransfer(psalMFC, srcBlock, destBlock);
}


static int api_mifare_login	(buffer_t* command, buffer_t* response){
	typedef struct __PACKED  {
		uint8_t key[6];
		uint8_t typeType;
		uint8_t sector;
	}login_param_t;
	login_param_t* login_param = (login_param_t*) command->data;
	uint8_t keyType = (login_param->typeType == 0) ? PHHAL_HW_MFC_KEYA : PHHAL_HW_MFC_KEYB;
	phStatus_t status = mifare_auth(login_param->sector, keyType, login_param->key);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}

static int api_mifare_read	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = net_buf_simple_tail(response);
	phStatus_t status = mifare_read(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	if(result){
		net_buf_simple_add(response, 16);
	}
	return ERR_NONE;
}

static int api_mifare_write	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = command->data;
	phStatus_t status = mifare_write(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}


static int api_mifare_read_value	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = net_buf_simple_tail(response);
	phStatus_t status = mifare_read_value(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	if(result){
		net_buf_simple_add(response, sizeof(uint32_t));
	}
	return ERR_NONE;
}

static int api_mifare_write_value	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = command->data;
	phStatus_t status = mifare_write_value(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}

static int api_mifare_increment_value	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = command->data;
	phStatus_t status = mifare_increment_value(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}
static int api_mifare_decrement_value	(buffer_t* command, buffer_t* response){
	uint8_t block = net_buf_simple_pull_u8(command);
	uint8_t* data = command->data;
	phStatus_t status = mifare_decrement_value(block, data);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}

static int api_mifare_copy_value	(buffer_t* command, buffer_t* response){
	uint8_t srcBlock = net_buf_simple_pull_u8(command);
	uint8_t destBlock = net_buf_simple_pull_u8(command);
	phStatus_t status =  mifare_copy_value(srcBlock, destBlock);
	uint8_t result = status ? 0 : 1;
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}

static const api_entry_t api_mifare_list[API_MIFARE_NUM] = {
	[API_MIFARE_LOGIN] = api_mifare_login,
	[API_MIFARE_READ_BLOCK] = api_mifare_read,
	[API_MIFARE_WRITE_BLOCK] = api_mifare_write,
	[API_MIFARE_READ_VALUE] = api_mifare_read_value,
	[API_MIFARE_WRITE_VALUE] = api_mifare_write_value,
	[API_MIFARE_INC_VALUE] = api_mifare_increment_value,
	[API_MIFARE_DEC_VALUE] = api_mifare_decrement_value,
	[API_MIFARE_COPY_VALUE] = api_mifare_copy_value,
};
	
static int mifare_api(buffer_t* command, buffer_t* response){
	const uint8_t num = sizeof(api_mifare_list)/sizeof(api_mifare_list[0]);
	return protocol_call_api(api_mifare_list, num, command, response);
}

SIMPLE_API_DEFINE(API_CLASS_MIFARE_CLASSIC, mifare_api);


